# -*- coding: cp1252 -*-
from SimPy.Simulation import *
from random import *

#Clase que instancia clientes y activa su PEM
class Source(Process):
    def generate(self, numClientes, res, mon, err):
        for i in range(numClientes):
            t1 = r.randint(1,480)           #Intervalo de espera entre clientes
            yield hold, self, t1            #Esperar
            c = Cliente("Cliente%02d"%i)    #Llega un cliente
            activate(c, c.visitarBancos(b=res, m=mon, e=err))   #Hace sus transacciones

#Clase que simula un cliente que visita un banco
class Cliente(Process):
    def visitarBancos(self, b, m, e):
            peopleVentanilla = []   #Lista que contiene la cantidad de gente en las ventanillas
            nmVentanillas = len(b)  #Contador de ventanillas
            #Variable que controla si todas las ventanillas del banco se llenaron
            #Es 0 ssi se llenaron todas las ventanillas
            todosLlenos = 1

            #Ver cuantas personas hay en cada ventanilla
            for i in range(nmVentanillas):
                peopleVentanilla.append(len(b[i].waitQ) + len(b[i].activeQ))

                #Si no hay gente en la ventanilla, no esta llena
                todosLlenos *= int(peopleVentanilla[i]!=0)

            #Con el monitor e, se observa cuando se llenaron todas las ventanillas
            e.observe(todosLlenos)

            #Escoger la ventanilla con menos personas
            for i in range(nmVentanillas):
                if peopleVentanilla[i] == 0 or peopleVentanilla[i] == min(peopleVentanilla):
                    self.ventanilla = i
                    break

            self.llega = now()                      #Registrar momento de llegada
            yield request,self,b[self.ventanilla]   #Solicitar la ventanilla escogida en el ciclo
            t2 = r.randint(1, 30)                   #Duracion de la transaccion
            yield hold,self,t2                      #Simular Transaccion
            tiempoTotal = now() - self.llega        #Calcular tiempo que se tardo en el banco
            m.observe(tiempoTotal)                  #Registrarlo en el monitor
            yield release,self,b[self.ventanilla]   #Liberar la ventanilla

def model():
    #Crear lista de ventanillas
    bancos = []
    for i in range(nmVentanillas):
        bancos.append(Resource(capacity=1, name = "Ventanilla %02d" %(i+1)))

    #Instanciar monitores
    monitor = Monitor()                 #Tiempo en el banco
    errores = Monitor(name="errores")   #Veces que se llenaron todas las ventanillas

    initialize()
    s = Source()

    #Activar el metodo para simular que esta en Source
    activate(s, s.generate(nmClientes, res=bancos, mon=monitor, err=errores), at=0.0)

    simulate(until=maxTime)

    #Devolver una lista con el numero de observaciones, la media, la desviacion estandar
    #y las veces que se llenaron todas las ventanillas
    return (monitor.count(), monitor.mean(), (monitor.var())**0.5, errores.total())

#Funcion para verificar que el ingreso del usuario es un numero entero positivo
def entDatosEnteros(mensaje):
    """
    Esta funcion verifica si el numero es entero.
    """
    error=True
    while error:
        x=raw_input(mensaje)
        try:
            x=int(x)
            if x>0:
                error = False
            else:
                print "ERROR: %s debe ser entero positivo.\n" %x
        except:
            print "ERROR: %s debe ser entero positivo.\n" %x
    return x


respuesta = "s"     #Controlador
theSeed = 12345     #Raiz del random

#Ciclo principal, mientras no quiera salir
while respuesta != "n":
    #Solicitar el numero de ventanillas
    nmVentanillas = entDatosEnteros("Ingrese el numero de ventanillas: ")

    #Numero de clientes con los que se correra la simulacion
    cantidades = [25, 50, 100, 150, 200]
    llenos = []     #lista que acumulara el numero de veces que se lleno el banco en cada simulacion

    #Para cada numero de clientes para la simulacion
    for nmClientes in cantidades:
        #Instanciar objeto Random
        r = Random(theSeed)

        #Calcular tiempo maximo de simulacion
        maxTime = nmClientes*510

        #Almacenar resultados
        resultado = model()

        #Mostrar media y desviacion estandar
        print "Media con %03d: %6.3f Desviación Estándar: %5.2f" %(nmClientes, resultado[1], resultado[2])

        #Almacenar el numero de veces que se lleno el banco y el numero de clientes
        llenos.append([nmClientes, resultado[3]])

    #Mostrar cuantas veces se lleno el banco por numero de clientes
    print "\nControl:"
    for dato in llenos:
        print "Con %3d clientes se lleno %2d veces" %(dato[0], dato[1])

    #Ciclo para consultar si quiere correr la simulación de nuevo
    hayError = True
    while hayError:
        respuesta = raw_input("\n¿Desea simular otra vez?: ")
        if respuesta == "N":
            respuesta = "n"
            hayError = False

        elif respuesta == "n" or respuesta == "s" or respuesta =="S":
            hayError = False

        else:
            print "ERROR: Ingrese 's' o 'n' solamente."
